/*
 *
 * 页面翻页
 *
 *
 */
(function(win){
    var doc = win.document;
    
    var extend = function(source, target, deep){
        for (var p in source) {
            if (source.hasOwnProperty(p)) {
                if (deep) {
                    target[p] = source[p];
                }
                else {
                    if (!target[p]) {
                        target[p] = source[p];
                    }
                }
            }
        }
        return target;
    }
    
    var isString = function(obj){
        if (!obj) {
            return false;
        }
        return Object.prototype.toString.call(obj) === "[object String]";
    }
    
    var bind = function(fun, context){
        var arg = Array.prototype.slice.call(arguments, 2);
        return function(){
            var args = arg.concat(arguments);
            return fun.apply(context, args);
        }
    }
    
    // findPos() by quirksmode.org
    // Finds the absolute position of an element on a page
    var findPos = function(obj){
        var curleft = curtop = 0;
        if (obj.offsetParent) {
            do {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
            }
            while (obj = obj.offsetParent);
        }
        return [curleft, curtop];
    };
    
    
    var config = {
        BOOK_WIDTH: 996,
        BOOK_HEIGHT: 260,
        PAGE_WIDTH: 400,
        PAGE_HEIGHT: 620,
        CANVAS_PADDING: 20
    }
    config.PAGE_Y = (config.BOOK_HEIGHT - config.PAGE_HEIGHT) / 2;
    
    var temp = {
        page: 0,
        mouse: {
            x: 0,
            y: 0
        },
        flips: []
    }
    
    var Fliper = function(){
        var param = arguments[0];
        if (!param) {
            return;
        }
        this.canvasElem = isString(param.canvas) ? doc.getElementById(param.canvas) : param.canvas;
        this.bookContainer = isString(param.book) ? doc.getElementById(param.book) : param.book;
        
        this.init();
    }
    Fliper.prototype = {
        constructor: Fliper,
        init: function(){
            var that = this;
            this.canvasContext = this.canvasElem.getContext("2d");
            this.pages = this.bookContainer.children;
            // Organize the depth of our pages and create the flip definitions
            for (var i = 0, len = this.pages.length; i < len; i++) {
                this.pages[i].style.zIndex = len - i;
                temp.flips.push({
                    // Current progress of the flip (left -1 to right +1)
                    progress: 1,
                    // The target value towards which progress is always moving
                    target: 1,
                    // The page DOM element related to this flip
                    page: this.pages[i],
                    // True while the page is being dragged
                    dragging: false
                });
            }
            // Resize the canvas to match the book size
            this.canvasElem.width = config.BOOK_WIDTH + (config.CANVAS_PADDING * 2);
            this.canvasElem.height = config.BOOK_HEIGHT + (config.CANVAS_PADDING * 2);
            
            // Offset the canvas so that it's padding is evenly spread around the book
            this.canvasElem.style.top = -config.CANVAS_PADDING + "px";
            this.canvasElem.style.left = -config.CANVAS_PADDING + "px";
            
            // Render the page flip 60 times a second
            setInterval(bind(that.render, that), 1000 / 60);
            
            doc.addEventListener("mousemove", bind(that.mouseMoveHandler, that), false);
            doc.addEventListener("mousedown", bind(that.mouseDownHandler, that), false);
            doc.addEventListener("mouseup", bind(that.mouseUpHandler, that), false);
            
        },
        mouseMoveHandler: function(evt){
            var that = this;
            var event = evt[0];
            var pos = findPos(that.bookContainer);
            // Offset mouse position so that the top of the spine is 0,0 
            // toTest: 有滚动条会不会出问题
            temp.mouse.x = event.clientX - pos[0] - (config.BOOK_WIDTH / 2);
            temp.mouse.y = event.clientY - pos[1];
        },
        mouseDownHandler: function(evt){
            var event = evt[0];
            if (Math.abs(temp.mouse.x) < config.PAGE_WIDTH) {
                if (temp.mouse.x < 0 && temp.page - 1 >= 0) {
                    temp.flips[temp.page - 1].dragging = true;
                }
                else 
                    if (temp.mouse.x > 0 && temp.page + 1 < temp.flips.length) {
                        temp.flips[temp.page].dragging = true;
                    }
            }
            // Prevents the text selection cursor from appearing when dragging
            event.preventDefault();
            
        },
        mouseUpHandler: function(event){
            for (var i = 0; i < temp.flips.length; i++) {
                // If this flip was being dragged we animate to its destination
                if (temp.flips[i].dragging) {
                    // Figure out which page we should go to next depending on the flip direction
                    if (temp.mouse.x < 0) {
                        temp.flips[i].target = -1;
                        temp.page = Math.min(temp.page + 1, temp.flips.length);
                    }
                    else {
                        temp.flips[i].target = 1;
                        temp.page = Math.max(temp.page - 1, 0);
                    }
                }
                temp.flips[i].dragging = false;
            }
        },
        render: function(){
            var that = this;
            this.canvasContext.clearRect(0, 0, this.canvasElem.width, this.canvasElem.height);
            for (var i = 0; i < temp.flips.length; i++) {
                var flip = temp.flips[i];
                if (flip.dragging) {
                    flip.target = Math.max(Math.min(temp.mouse.x / config.PAGE_WIDTH, 1), -1);
                    //console.log(flip.target);
                }
                
                flip.progress += (flip.target - flip.progress) * .10;
                
                
                // If the flip is being dragged or is somewhere in the middle of the book, render it
                if (flip.dragging || Math.abs(flip.progress) < 0.997) {
                
                    (bind(that.drawFlip, that))(flip);
                }
                
            }
        },
        drawFlip: function(flip){
			var PAGE_HEIGHT=config.PAGE_HEIGHT;
			var PAGE_WIDTH = config.PAGE_WIDTH;
			var CANVAS_PADDING = config.CANVAS_PADDING;
			var PAGE_Y = config.PAGE_Y;
			var context = this.canvasContext;
			var BOOK_WIDTH = config.BOOK_WIDTH;
			
            flip = flip[0];
            // todo:提取变量
            // Strength of the fold is strongest in the middle of the book
            var strength = 1 - Math.abs(flip.progress);
            
            // Width of the folded paper
            var foldWidth = (PAGE_WIDTH * 0.5) * (1 - flip.progress);
            
            // X position of the folded paper
            var foldX = PAGE_WIDTH * flip.progress + foldWidth;
            
            // How far the page should outdent vertically due to perspective
            var verticalOutdent = 20 * strength;
            
            // The maximum width of the left and right side shadows
            var paperShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(1 - flip.progress, 0.5), 0);
            var rightShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(strength, 0.5), 0);
            var leftShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(strength, 0.5), 0);
            
            
            // Change page element width to match the x position of the fold
            flip.page.style.width = Math.max(foldX, 0) + "px";
            
            context.save();
            context.translate(CANVAS_PADDING + (BOOK_WIDTH / 2), PAGE_Y + CANVAS_PADDING);
            
            
            // Draw a sharp shadow on the left side of the page
            context.strokeStyle = 'rgba(0,0,0,' + (0.05 * strength) + ')';
            context.lineWidth = 30 * strength;
            context.beginPath();
            context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5);
            context.lineTo(foldX - foldWidth, PAGE_HEIGHT + (verticalOutdent * 0.5));
            context.stroke();
            
            
            // Right side drop shadow
            var rightShadowGradient = context.createLinearGradient(foldX, 0, foldX + rightShadowWidth, 0);
            rightShadowGradient.addColorStop(0, 'rgba(0,0,0,' + (strength * 0.2) + ')');
            rightShadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.0)');
            
            context.fillStyle = rightShadowGradient;
            context.beginPath();
            context.moveTo(foldX, 0);
            context.lineTo(foldX + rightShadowWidth, 0);
            context.lineTo(foldX + rightShadowWidth, PAGE_HEIGHT);
            context.lineTo(foldX, PAGE_HEIGHT);
            context.fill();
            
            
            // Left side drop shadow
            var leftShadowGradient = context.createLinearGradient(foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0);
            leftShadowGradient.addColorStop(0, 'rgba(0,0,0,0.0)');
            leftShadowGradient.addColorStop(1, 'rgba(0,0,0,' + (strength * 0.15) + ')');
            
            context.fillStyle = leftShadowGradient;
            context.beginPath();
            context.moveTo(foldX - foldWidth - leftShadowWidth, 0);
            context.lineTo(foldX - foldWidth, 0);
            context.lineTo(foldX - foldWidth, PAGE_HEIGHT);
            context.lineTo(foldX - foldWidth - leftShadowWidth, PAGE_HEIGHT);
            context.fill();
            
            
            // Gradient applied to the folded paper (highlights & shadows)
            var foldGradient = context.createLinearGradient(foldX - paperShadowWidth, 0, foldX, 0);
            foldGradient.addColorStop(0.35, '#fafafa');
            foldGradient.addColorStop(0.73, '#eeeeee');
            foldGradient.addColorStop(0.9, '#fafafa');
            foldGradient.addColorStop(1.0, '#e2e2e2');
            
            context.fillStyle = foldGradient;
            context.strokeStyle = 'rgba(0,0,0,0.06)';
            context.lineWidth = 0.5;
            
            // Draw the folded piece of paper
            context.beginPath();
            context.moveTo(foldX, 0);
            context.lineTo(foldX, PAGE_HEIGHT);
            context.quadraticCurveTo(foldX, PAGE_HEIGHT + (verticalOutdent * 2), foldX - foldWidth, PAGE_HEIGHT + verticalOutdent);
            context.lineTo(foldX - foldWidth, -verticalOutdent);
            context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0);
            
            context.fill();
            context.stroke();
            
            
            context.restore();
        }
        
        
    }
    win.Fliper = Fliper;
    
})(window);
